package com.diandian.backend.entrance.service;

import com.alibaba.fastjson.JSONObject;
import com.diandian.backend.entrance.constants.EntranceConstant;
import com.diandian.backend.entrance.dto.ClientPermissionCacheDataDTO;
import com.diandian.base.boot.starter.dto.CheckSignCommonResultDTO;
import com.diandian.base.boot.starter.exception.DefaultException;
import com.diandian.base.boot.starter.util.SignVerifyUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.StringJoiner;

@Service
public class ClientSecretCheckService {

    private static final Logger logger = LoggerFactory.getLogger(ClientSecretCheckService.class);

    @Autowired
    private RedisTemplate redisTemplate;

    private static final String CLIENT_PERMISSION_CACHE_PREFIX = "client:permission:cache:";

    /**
     * 校验客户端的有效性和合法性: 从redis中获取，有则有，无则无
     *
     *      "clientKey": {
     *          "clientSecret":"redisTemplate",
     *          "status": "status",
     *          "startTime":"yyyy-MM-dd HH:mm:ss",
     *          "endTime":"yyyy-MM-dd HH:mm:ss"，
     *          “cmds”:[
     *              "cmd1",
     *              "cmd2"
     *          ]
     *      }
     *      1. 校验clientId和clientSecret是否正确
     *      2. 校验client是否为启用状态且在有效期内
     *      3. 校验client是否有对应cmd的权限
     *
     * @param clientKey
     * @param clientSecret
     */
    public ClientPermissionCacheDataDTO checkV1(String clientKey, String clientSecret, String cmd) {

        // 从redis中获取数据
        Object cacheData = redisTemplate.opsForValue().get(CLIENT_PERMISSION_CACHE_PREFIX + clientKey);
        if (cacheData == null) {
            throw new DefaultException("客户端信息错误");
        }

        // 数据转换
        ClientPermissionCacheDataDTO permissionCacheDataDTO = JSONObject.parseObject(cacheData.toString(), ClientPermissionCacheDataDTO.class);

        // 校验clientId和clientSecret是否正确
        if(!permissionCacheDataDTO.getClientSecret().equals(clientSecret)){
            throw new DefaultException("客户端信息错误");
        }

        String status = permissionCacheDataDTO.getStatus();
        if(EntranceConstant.PERMISSION_STATUS_DISABLE.equals(status)){
            throw new DefaultException("客户端已禁用");
        }

        Date startTime = permissionCacheDataDTO.getStartTime();
        if(new Date().before(startTime)){
            throw new DefaultException("客户端暂未生效");
        }
        Date endTime = permissionCacheDataDTO.getEndTime();
        if(new Date().after(endTime)){
            throw new DefaultException("客户端已失效");
        }

        // 校验client是否有对应cmd的权限
        List<String> cmdList = permissionCacheDataDTO.getCmds();
        if(cmdList == null || cmdList.size() == 0){
            throw new DefaultException("无操作权限");
        }

        // 是否包含当前cmd
        boolean containsCmdFlag = cmdList.contains(cmd);
        boolean containsSpecialFlag = containsSpecialFlag(cmdList, cmd);

        if(!containsCmdFlag && !containsSpecialFlag){
            throw new DefaultException("无权限执行此操作");
        }

        return permissionCacheDataDTO;
    }

    /**
     * 验签
     * @param clientKey
     * @param clientSecret
     * @param cmd
     * @return
     */
    public ClientPermissionCacheDataDTO checkV2(String clientKey, String clientSecret, String cmd, String dataJson, String time, String sourceSign) {

        // 签名验证
        CheckSignCommonResultDTO signCommonResultDTO = SignVerifyUtil.checkSign(sourceSign, clientKey, clientSecret, time, dataJson);
        if(SignVerifyUtil.SIGN_CHECK_FAIL == signCommonResultDTO.getCode()){
            throw new DefaultException(signCommonResultDTO.getMessage());
        }

        // 从redis中获取数据
        Object cacheData = redisTemplate.opsForValue().get(CLIENT_PERMISSION_CACHE_PREFIX + clientKey);
        if (cacheData == null) {
            throw new DefaultException("客户端信息错误");
        }

        // 数据转换
        ClientPermissionCacheDataDTO permissionCacheDataDTO = JSONObject.parseObject(cacheData.toString(), ClientPermissionCacheDataDTO.class);

        // 校验clientId和clientSecret是否正确
        if(!permissionCacheDataDTO.getClientSecret().equals(clientSecret)){
            throw new DefaultException("客户端信息错误");
        }

        String status = permissionCacheDataDTO.getStatus();
        if(EntranceConstant.PERMISSION_STATUS_DISABLE.equals(status)){
            throw new DefaultException("客户端已禁用");
        }

        Date startTime = permissionCacheDataDTO.getStartTime();
        if(new Date().before(startTime)){
            throw new DefaultException("客户端暂未生效");
        }
        Date endTime = permissionCacheDataDTO.getEndTime();
        if(new Date().after(endTime)){
            throw new DefaultException("客户端已失效");
        }

        // 校验client是否有对应cmd的权限
        List<String> cmdList = permissionCacheDataDTO.getCmds();
        if(cmdList == null || cmdList.size() == 0){
            throw new DefaultException("无操作权限");
        }

        // 是否包含当前cmd
        boolean containsCmdFlag = cmdList.contains(cmd);
        boolean containsSpecialFlag = containsSpecialFlag(cmdList, cmd);

        if(!containsCmdFlag && !containsSpecialFlag){
            throw new DefaultException("无权限执行此操作");
        }

        return permissionCacheDataDTO;
    }

    /**
     * 校验是否包含特殊置顶权限（带有.*）的权限
     * @param cmdList
     * @param cmd
     * @return
     */
    private boolean containsSpecialFlag(List<String> cmdList, String cmd){

        if(cmdList == null || cmdList.size() == 0){
            return false;
        }
        if(cmd == null || "".equals(cmd)){
            return false;
        }

        String[] cmdArr = cmd.split("\\.");
        StringJoiner stringJoiner = new StringJoiner(".");
        for (int i = 0; i < cmdArr.length; i++) {
            stringJoiner.add(cmdArr[i]);
            String tempCmd = stringJoiner.toString() + ".*";
            if(cmdList.contains(tempCmd)){
                return true;
            }
        }
        return false;
    }
}
